Interactive graphics with plotly

install.packages("plotly")

First Interactive Plot

library(tidyverse)
library(plotly)
p <- ggplot(data = midwest) +
  geom_point(mapping = aes(x = popdensity, y = percollege))
ggplotly(p)

Customized Interactive Plot

p <- ggplot(midwest, 
       aes(x = popdensity, y = percollege, color = state)) +
  geom_point() + 
  scale_x_continuous("Population Density", 
                     breaks = seq(0, 80000, 20000)) + 
  scale_y_continuous("Percent College Graduates") + 
  scale_color_discrete("State") + 
  theme_bw()
ggplotly(p)

Your Turn

  1. Using the starwars data, create a static ggplot and use the ggplotly function to turn it interactive.

Lord of the Rings Data

lotr <- read_tsv('https://raw.githubusercontent.com/jennybc/lotr/master/lotr_clean.tsv')
lotr

Create plotly by hand

plot_ly(lotr, x = ~Words) %>% add_histogram()

Subplots

one_plot <- function(d) {
  plot_ly(d, x = ~Words) %>%
    add_histogram() %>%
    add_annotations(
      ~unique(Film), x = 0.5, y = 1, 
      xref = "paper", yref = "paper", showarrow = FALSE
    )
}

lotr %>%
  split(.$Film) %>%
  lapply(one_plot) %>% 
  subplot(nrows = 1, shareX = TRUE, titleX = FALSE) %>%
  hide_legend()

Grouped bar plot

plot_ly(lotr, x = ~Race, color = ~Film) %>% add_histogram()

Plot of proportions

## number of diamonds by cut and clarity (n)
lotr_count <- count(lotr, Race, Film)
## number of diamonds by cut (nn)
lotr_prop <- left_join(lotr_count, count(lotr_count, Race, wt = n), 
                       by = 'Race')

lotr_prop %>%
  mutate(prop = n.x / n.y) %>%
  plot_ly(x = ~Race, y = ~prop, color = ~Film, width = 900) %>%
  add_bars() %>%
  layout(barmode = "stack")

Your Turn

  1. Using the gss_cat data, create a histrogram for the tvhours variable.
  2. Using the gss_cat data, create a bar chart showing the partyid variable by the marital status.

Scatterplots by Hand

plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
  add_markers()

Change symbol

plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
  add_markers(symbol = ~state)

Change color

plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
  add_markers(color = ~state, colors = viridis::viridis(5))

Line Graph

storms_yearly <- storms %>%
  group_by(year) %>%
  summarise(num = length(unique(name)))

plot_ly(storms_yearly, x = ~year, y = ~num) %>%
  add_lines()

Your Turn

  1. Using the gss_cat data, create a scatterplot showing the age and tvhours variables.
  2. Compute the average time spent watching tv by year and marital status. Then, plot the average time spent watching tv by year and marital status.

Highcharter; Highcharts for R

devtools::install_github("jbkunst/highcharter")

hchart function

library(highcharter)
Highcharts (www.highcharts.com) is a Highsoft software product which is
not free for commercial and Governmental use
lotr_count <- lotr %>%
  count(Film, Race)

hchart(lotr_count, "column", hcaes(x = Race, y = n, group = Film))

A second hchart

hchart(midwest, "scatter", hcaes(x = popdensity, y = percollege, group = state))

Histogram

hchart(lotr$Words)

Your Turn

  1. Using the hchart function, create a bar chart or histogram with the gss_cat data.
  2. Using the hchart function, create a scatterplot with the gss_cat data.

Build Highcharts from scratch

hc <- highchart() %>%
  hc_xAxis(categories = lotr_count$Race) %>%
  hc_add_series(name = 'The Fellowship Of The Ring', 
                data = filter(lotr_count, Film == 'The Fellowship Of The Ring')$n) %>% 
  hc_add_series(name = 'The Two Towers', 
                data = filter(lotr_count, Film == 'The Two Towers')$n) %>%
  hc_add_series(name = 'The Return Of The King', 
                data = filter(lotr_count, Film == 'The Return Of The King')$n)
hc

Change Chart type

hc <- hc %>%
  hc_chart(type = 'column')
hc

Change Colors

hc <- hc %>%
  hc_colors(substr(viridis::viridis(3), 0, 7))
hc

Modify Axes

hc <- hc %>%
  hc_xAxis(title = list(text = "Race")) %>%
  hc_yAxis(title = list(text = "Number of Words Spoken"),
           showLastLabel = FALSE)
hc

Add title, subtitle, move legend

hc <- hc %>%
  hc_title(text = 'Number of Words Spoken in Lord of the Rings Films',
           align = 'left') %>%
  hc_subtitle(text = 'Broken down by <i>Film</i> and <b>Race</b>', 
              align = 'left') %>%
  hc_legend(align = 'right', verticalAlign = 'top', layout = 'vertical',
            x = 0, y = 80) %>%
  hc_exporting(enabled = TRUE)
hc

Your Turn

  1. Build up a plot from scratch, getting the figure close to publication quality using the gss_cat data.

Correlation Matrices

select(storms, wind, pressure, ts_diameter, hu_diameter) %>%
  cor(use = "pairwise.complete.obs") %>%
  hchart()

Leaflet Example

library(leaflet)

storms %>%
  filter(name %in% c('Ike', 'Katrina'), year > 2000) %>%
  leaflet() %>%
  addTiles() %>%
  addCircles(lng = ~long, lat = ~lat, popup = ~name, weight = 1,
             radius = ~wind*1000)

Additional Resources

LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIC0gSW50ZXJhY3RpdmUgR3JhcGhpY3MgdXNpbmcgUiIKYXV0aG9yOiAiQnJhbmRvbiBMZUJlYXUiCmRhdGU6ICJGZWJydWFyeSAyMSwgMjAxOSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXBfY2h1bmtzLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NiwgZmlnLmNhcCA9IE5VTEwpIApgYGAKCiMgSW50ZXJhY3RpdmUgZ3JhcGhpY3Mgd2l0aCBwbG90bHkKYGBge3IgaW5zdGFsbCwgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQpgYGAKCiMgRmlyc3QgSW50ZXJhY3RpdmUgUGxvdApgYGB7ciBmaXJzdF9wbG90bHksIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBsb3RseSkKcCA8LSBnZ3Bsb3QoZGF0YSA9IG1pZHdlc3QpICsKICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSkKZ2dwbG90bHkocCkKYGBgCgojIEN1c3RvbWl6ZWQgSW50ZXJhY3RpdmUgUGxvdApgYGB7ciBjdXN0b21fcGxvdGx5LCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQpwIDwtIGdncGxvdChtaWR3ZXN0LCAKICAgICAgIGFlcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UsIGNvbG9yID0gc3RhdGUpKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKCJQb3B1bGF0aW9uIERlbnNpdHkiLCAKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDgwMDAwLCAyMDAwMCkpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKCJQZXJjZW50IENvbGxlZ2UgR3JhZHVhdGVzIikgKyAKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgiU3RhdGUiKSArIAogIHRoZW1lX2J3KCkKZ2dwbG90bHkocCkKYGBgCgojIFlvdXIgVHVybgoxLiBVc2luZyB0aGUgYHN0YXJ3YXJzYCBkYXRhLCBjcmVhdGUgYSBzdGF0aWMgZ2dwbG90IGFuZCB1c2UgdGhlIGBnZ3Bsb3RseWAgZnVuY3Rpb24gdG8gdHVybiBpdCBpbnRlcmFjdGl2ZS4gCgojIExvcmQgb2YgdGhlIFJpbmdzIERhdGEKLSBEYXRhIGZyb20gSmVubnkgQnJ5YW46IDxodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9sb3RyPgoKYGBge3IgcmVhZF9pbl9sb3RyLCBlcnJvciA9IEZBTFNFfQpsb3RyIDwtIHJlYWRfdHN2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vamVubnliYy9sb3RyL21hc3Rlci9sb3RyX2NsZWFuLnRzdicpCmxvdHIKYGBgCgojIENyZWF0ZSBwbG90bHkgYnkgaGFuZApgYGB7ciBwbG90bHlfYnlfaGFuZCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9CnBsb3RfbHkobG90ciwgeCA9IH5Xb3JkcykgJT4lIGFkZF9oaXN0b2dyYW0oKQpgYGAKCiMgU3VicGxvdHMKYGBge3Igc3VicGxvdHMsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQpvbmVfcGxvdCA8LSBmdW5jdGlvbihkKSB7CiAgcGxvdF9seShkLCB4ID0gfldvcmRzKSAlPiUKICAgIGFkZF9oaXN0b2dyYW0oKSAlPiUKICAgIGFkZF9hbm5vdGF0aW9ucygKICAgICAgfnVuaXF1ZShGaWxtKSwgeCA9IDAuNSwgeSA9IDEsIAogICAgICB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHNob3dhcnJvdyA9IEZBTFNFCiAgICApCn0KCmxvdHIgJT4lCiAgc3BsaXQoLiRGaWxtKSAlPiUKICBsYXBwbHkob25lX3Bsb3QpICU+JSAKICBzdWJwbG90KG5yb3dzID0gMSwgc2hhcmVYID0gVFJVRSwgdGl0bGVYID0gRkFMU0UpICU+JQogIGhpZGVfbGVnZW5kKCkKYGBgCgoKIyBHcm91cGVkIGJhciBwbG90CmBgYHtyIHBsb3RseV9ncm91cCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9CnBsb3RfbHkobG90ciwgeCA9IH5SYWNlLCBjb2xvciA9IH5GaWxtKSAlPiUgYWRkX2hpc3RvZ3JhbSgpCmBgYAoKIyBQbG90IG9mIHByb3BvcnRpb25zCmBgYHtyIHBsb3RseV9wcm9wb3J0aW9ucywgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KIyMgbnVtYmVyIG9mIGRpYW1vbmRzIGJ5IGN1dCBhbmQgY2xhcml0eSAobikKbG90cl9jb3VudCA8LSBjb3VudChsb3RyLCBSYWNlLCBGaWxtKQojIyBudW1iZXIgb2YgZGlhbW9uZHMgYnkgY3V0IChubikKbG90cl9wcm9wIDwtIGxlZnRfam9pbihsb3RyX2NvdW50LCBjb3VudChsb3RyX2NvdW50LCBSYWNlLCB3dCA9IG4pLCAKICAgICAgICAgICAgICAgICAgICAgICBieSA9ICdSYWNlJykKCmxvdHJfcHJvcCAlPiUKICBtdXRhdGUocHJvcCA9IG4ueCAvIG4ueSkgJT4lCiAgcGxvdF9seSh4ID0gflJhY2UsIHkgPSB+cHJvcCwgY29sb3IgPSB+RmlsbSwgd2lkdGggPSA5MDApICU+JQogIGFkZF9iYXJzKCkgJT4lCiAgbGF5b3V0KGJhcm1vZGUgPSAic3RhY2siKQpgYGAKCiMgWW91ciBUdXJuCjEuIFVzaW5nIHRoZSBgZ3NzX2NhdGAgZGF0YSwgY3JlYXRlIGEgaGlzdHJvZ3JhbSBmb3IgdGhlIGB0dmhvdXJzYCB2YXJpYWJsZS4gCjIuIFVzaW5nIHRoZSBgZ3NzX2NhdGAgZGF0YSwgY3JlYXRlIGEgYmFyIGNoYXJ0IHNob3dpbmcgdGhlIGBwYXJ0eWlkYCB2YXJpYWJsZSBieSB0aGUgYG1hcml0YWxgIHN0YXR1cy4KCiMgU2NhdHRlcnBsb3RzIGJ5IEhhbmQKYGBge3IgcGxvdGx5X3NjYXR0ZXIsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2LCB3YXJuaW5nID0gRkFMU0V9CnBsb3RfbHkobWlkd2VzdCwgeCA9IH5wb3BkZW5zaXR5LCB5ID0gfnBlcmNvbGxlZ2UpICU+JQogIGFkZF9tYXJrZXJzKCkKYGBgCgojIENoYW5nZSBzeW1ib2wKYGBge3IgcGxvdGx5X3N5bWJvbCwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9CnBsb3RfbHkobWlkd2VzdCwgeCA9IH5wb3BkZW5zaXR5LCB5ID0gfnBlcmNvbGxlZ2UpICU+JQogIGFkZF9tYXJrZXJzKHN5bWJvbCA9IH5zdGF0ZSkKYGBgCgojIENoYW5nZSBjb2xvcgpgYGB7ciBwbG90bHlfY29sb3IsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQpwbG90X2x5KG1pZHdlc3QsIHggPSB+cG9wZGVuc2l0eSwgeSA9IH5wZXJjb2xsZWdlKSAlPiUKICBhZGRfbWFya2Vycyhjb2xvciA9IH5zdGF0ZSwgY29sb3JzID0gdmlyaWRpczo6dmlyaWRpcyg1KSkKYGBgCgojIExpbmUgR3JhcGgKYGBge3IgcGxvdGx5X2xpbmUsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQpzdG9ybXNfeWVhcmx5IDwtIHN0b3JtcyAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpc2UobnVtID0gbGVuZ3RoKHVuaXF1ZShuYW1lKSkpCgpwbG90X2x5KHN0b3Jtc195ZWFybHksIHggPSB+eWVhciwgeSA9IH5udW0pICU+JQogIGFkZF9saW5lcygpCmBgYAoKIyBZb3VyIFR1cm4KMS4gVXNpbmcgdGhlIGBnc3NfY2F0YCBkYXRhLCBjcmVhdGUgYSBzY2F0dGVycGxvdCBzaG93aW5nIHRoZSBgYWdlYCBhbmQgYHR2aG91cnNgIHZhcmlhYmxlcy4KMi4gQ29tcHV0ZSB0aGUgYXZlcmFnZSB0aW1lIHNwZW50IHdhdGNoaW5nIHR2IGJ5IHllYXIgYW5kIG1hcml0YWwgc3RhdHVzLiBUaGVuLCBwbG90IHRoZSBhdmVyYWdlIHRpbWUgc3BlbnQgd2F0Y2hpbmcgdHYgYnkgeWVhciBhbmQgbWFyaXRhbCBzdGF0dXMuCgojIEhpZ2hjaGFydGVyOyBIaWdoY2hhcnRzIGZvciBSCmBgYHtyIGluc3RhbGxfaGlnaGNoYXJ0ZXIsIGV2YWwgPSBGQUxTRX0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJqYmt1bnN0L2hpZ2hjaGFydGVyIikKYGBgCgojIGBoY2hhcnRgIGZ1bmN0aW9uCmBgYHtyIGhjaGFydDEsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQoKbG90cl9jb3VudCA8LSBsb3RyICU+JQogIGNvdW50KEZpbG0sIFJhY2UpCgpoY2hhcnQobG90cl9jb3VudCwgImNvbHVtbiIsIGhjYWVzKHggPSBSYWNlLCB5ID0gbiwgZ3JvdXAgPSBGaWxtKSkKYGBgCgojIEEgc2Vjb25kIGBoY2hhcnRgCmBgYHtyIGhjaGFydDIsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQpoY2hhcnQobWlkd2VzdCwgInNjYXR0ZXIiLCBoY2Flcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UsIGdyb3VwID0gc3RhdGUpKQpgYGAKCiMgSGlzdG9ncmFtCmBgYHtyIGhjaGFydF9oaXN0LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0KaGNoYXJ0KGxvdHIkV29yZHMpCmBgYAoKIyBZb3VyIFR1cm4KMS4gVXNpbmcgdGhlIGBoY2hhcnRgIGZ1bmN0aW9uLCBjcmVhdGUgYSBiYXIgY2hhcnQgb3IgaGlzdG9ncmFtIHdpdGggdGhlIGBnc3NfY2F0YCBkYXRhLgoyLiBVc2luZyB0aGUgYGhjaGFydGAgZnVuY3Rpb24sIGNyZWF0ZSBhIHNjYXR0ZXJwbG90IHdpdGggdGhlIGBnc3NfY2F0YCBkYXRhLgoKIyBCdWlsZCBIaWdoY2hhcnRzIGZyb20gc2NyYXRjaApgYGB7ciBoY19zY3JhdGNoLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0KaGMgPC0gaGlnaGNoYXJ0KCkgJT4lCiAgaGNfeEF4aXMoY2F0ZWdvcmllcyA9IGxvdHJfY291bnQkUmFjZSkgJT4lCiAgaGNfYWRkX3NlcmllcyhuYW1lID0gJ1RoZSBGZWxsb3dzaGlwIE9mIFRoZSBSaW5nJywgCiAgICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKGxvdHJfY291bnQsIEZpbG0gPT0gJ1RoZSBGZWxsb3dzaGlwIE9mIFRoZSBSaW5nJykkbikgJT4lIAogIGhjX2FkZF9zZXJpZXMobmFtZSA9ICdUaGUgVHdvIFRvd2VycycsIAogICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihsb3RyX2NvdW50LCBGaWxtID09ICdUaGUgVHdvIFRvd2VycycpJG4pICU+JQogIGhjX2FkZF9zZXJpZXMobmFtZSA9ICdUaGUgUmV0dXJuIE9mIFRoZSBLaW5nJywgCiAgICAgICAgICAgICAgICBkYXRhID0gZmlsdGVyKGxvdHJfY291bnQsIEZpbG0gPT0gJ1RoZSBSZXR1cm4gT2YgVGhlIEtpbmcnKSRuKQpoYwpgYGAKCiMgQ2hhbmdlIENoYXJ0IHR5cGUKYGBge3IgaGNfY2hhcnQsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQpoYyA8LSBoYyAlPiUKICBoY19jaGFydCh0eXBlID0gJ2NvbHVtbicpCmhjCmBgYAoKIyBDaGFuZ2UgQ29sb3JzCmBgYHtyIGhjX2NoYW5nZV9jb2xvcnMsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQpoYyA8LSBoYyAlPiUKICBoY19jb2xvcnMoc3Vic3RyKHZpcmlkaXM6OnZpcmlkaXMoMyksIDAsIDcpKQpoYwpgYGAKCiMgTW9kaWZ5IEF4ZXMKYGBge3IgaGNfYXhpcywgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9CmhjIDwtIGhjICU+JQogIGhjX3hBeGlzKHRpdGxlID0gbGlzdCh0ZXh0ID0gIlJhY2UiKSkgJT4lCiAgaGNfeUF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiTnVtYmVyIG9mIFdvcmRzIFNwb2tlbiIpLAogICAgICAgICAgIHNob3dMYXN0TGFiZWwgPSBGQUxTRSkKaGMKYGBgCgojIEFkZCB0aXRsZSwgc3VidGl0bGUsIG1vdmUgbGVnZW5kCmBgYHtyIGhjX21vZGlmeSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9CmhjIDwtIGhjICU+JQogIGhjX3RpdGxlKHRleHQgPSAnTnVtYmVyIG9mIFdvcmRzIFNwb2tlbiBpbiBMb3JkIG9mIHRoZSBSaW5ncyBGaWxtcycsCiAgICAgICAgICAgYWxpZ24gPSAnbGVmdCcpICU+JQogIGhjX3N1YnRpdGxlKHRleHQgPSAnQnJva2VuIGRvd24gYnkgPGk+RmlsbTwvaT4gYW5kIDxiPlJhY2U8L2I+JywgCiAgICAgICAgICAgICAgYWxpZ24gPSAnbGVmdCcpICU+JQogIGhjX2xlZ2VuZChhbGlnbiA9ICdyaWdodCcsIHZlcnRpY2FsQWxpZ24gPSAndG9wJywgbGF5b3V0ID0gJ3ZlcnRpY2FsJywKICAgICAgICAgICAgeCA9IDAsIHkgPSA4MCkgJT4lCiAgaGNfZXhwb3J0aW5nKGVuYWJsZWQgPSBUUlVFKQpoYwpgYGAKCgojIFlvdXIgVHVybgoxLiBCdWlsZCB1cCBhIHBsb3QgZnJvbSBzY3JhdGNoLCBnZXR0aW5nIHRoZSBmaWd1cmUgY2xvc2UgdG8gcHVibGljYXRpb24gcXVhbGl0eSB1c2luZyB0aGUgYGdzc19jYXRgIGRhdGEuCgojIENvcnJlbGF0aW9uIE1hdHJpY2VzCmBgYHtyIGNvcnJlbGF0aW9ufQpzZWxlY3Qoc3Rvcm1zLCB3aW5kLCBwcmVzc3VyZSwgdHNfZGlhbWV0ZXIsIGh1X2RpYW1ldGVyKSAlPiUKICBjb3IodXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpICU+JQogIGhjaGFydCgpCmBgYAoKIyBMZWFmbGV0IEV4YW1wbGUKYGBge3IgbGVhZmxldH0KbGlicmFyeShsZWFmbGV0KQoKc3Rvcm1zICU+JQogIGZpbHRlcihuYW1lICVpbiUgYygnSWtlJywgJ0thdHJpbmEnKSwgeWVhciA+IDIwMDApICU+JQogIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygpICU+JQogIGFkZENpcmNsZXMobG5nID0gfmxvbmcsIGxhdCA9IH5sYXQsIHBvcHVwID0gfm5hbWUsIHdlaWdodCA9IDEsCiAgICAgICAgICAgICByYWRpdXMgPSB+d2luZCoxMDAwKQpgYGAKCgojIEFkZGl0aW9uYWwgUmVzb3VyY2VzCiogcGxvdGx5IGZvciBSIGJvb2s6IDxodHRwczovL3Bsb3RseS1ib29rLmNwc2lldmVydC5tZS8+CiogcGxvdGx5OiA8aHR0cHM6Ly9wbG90Lmx5Lz4KKiBoaWdoY2hhcnRlcjogPGh0dHA6Ly9qa3Vuc3QuY29tL2hpZ2hjaGFydGVyL2luZGV4Lmh0bWw+CiogaGlnaGNoYXJ0czogPGh0dHBzOi8vd3d3LmhpZ2hjaGFydHMuY29tLz4KKiBodG1sd2lkZ2V0czogPGh0dHBzOi8vd3d3Lmh0bWx3aWRnZXRzLm9yZy8+Cg==